﻿#include	"Engine.File.hpp"
#include	"Engine.String.hpp"

#include	<cstdio>
#include	<cstdlib>
#include	<cstring>
#include	<fstream>

namespace Engine { namespace File {

	bool CsvFile::Load(const char * szFile, SplitType emType /* = SplitType::Tab */) {
		if (!szFile || ::strlen(szFile) <= 0) return false;

		char	nSpec	= (emType == SplitType::Tab ? '\t' : ',');
		char *	pStore	= new (std::nothrow) char[1024 * 64];
		size_t	nStart	= 0;
		size_t	nEnd	= 0;
		size_t	nCol	= 0;
		size_t	nNoname	= 0;
		size_t	nLine	= 0;
		bool	bTitle	= true;
		FILE *	pFile	= NULL;

		if (!pStore) return false;

#if defined(_WIN32) && defined(_MSC_VER)
		if (fopen_s(&pFile, szFile, "r") != 0) return false;
#else
		if ((pFile = fopen(szFile, "r")) == NULL) return false;
#endif

		unsigned char pBomb[3] = { 0, 0, 0 };
		int nCount = fread(pBomb, 1, 3, pFile);
		if (nCount < 3 || !(pBomb[0] == 0xEF && pBomb[1] == 0xBB && pBomb[2] == 0xBF)) {
			fseek(pFile, 0, SEEK_SET);
		}

		while (!feof(pFile)) {
			::memset(pStore, 0, 1024 * 64);
			fgets(pStore, 1024 * 64, pFile);

			int nLen = strlen(pStore);
			if (nLen <= 0) {
				break;
			} else if (nLen >= 1024 * 64) {
				fclose(pFile);
				return false;
			}

			nStart = 0, nEnd = 0, nCol = 0;

			if (!bTitle) nLine = AddLine();

			while (pStore[nEnd] && pStore[nEnd] != '\n') {
				if (pStore[nEnd] == nSpec) {
					if (nStart == nEnd) {
						if (bTitle) {
							std::string szValue = String::Format("Csv_Un_Named_Title_%d", nNoname);
							++nNoname;
							_vKeys.push_back(szValue);
						}
					} else {
						std::string szValue(pStore + nStart, nEnd - nStart);
						if (bTitle) _vKeys.push_back(szValue);
						else _vContent[nLine][nCol] = szValue;
					}
					nStart = nEnd + 1;
					++nCol;
				}
				++nEnd;
			}

			if (nStart == nEnd) {
				if (bTitle) {
					std::string szValue = String::Format("Csv_Un_Named_Title_%d", nNoname);
					++nNoname;
					_vKeys.push_back(szValue);
				}
			} else {
				std::string szValue(pStore + nStart, nEnd - nStart);
				if (bTitle) _vKeys.push_back(szValue);
				else _vContent[nLine][nCol] = szValue;
			}

			bTitle = false;
		}

		fclose(pFile);
		return true;
	}

	bool CsvFile::Save(const char * szFile, SplitType emType /* = SplitType::Tab */) {
		const std::string szSpec = emType == SplitType::Tab ? "\t" : ",";

		if (!szFile || ::strlen(szFile) <= 0) return false;

		std::ofstream iFile(szFile, std::ifstream::out);
		if (!iFile.is_open()) return false;

		for (auto itorKeys = _vKeys.begin(); itorKeys != _vKeys.end();) {
			iFile << *itorKeys;
			++itorKeys;
			if (itorKeys != _vKeys.end()) iFile << szSpec;
			else iFile << std::endl;
		}

		for (auto itorLine = _vContent.begin(); itorLine != _vContent.end(); ++itorLine) {
			for (auto itorCol = itorLine->begin(); itorCol != itorLine->end();) {
				iFile << *itorCol;
				++itorCol;
				if (itorCol != itorLine->end()) iFile << szSpec;
				else iFile << std::endl;
			}
		}

		return true;
	}

	std::string CsvFile::GetColumnName(int nIdx) {
		if (nIdx < 0 || (size_t) nIdx >= _vKeys.size()) return std::string("");
		return _vKeys[nIdx];
	}

	int CsvFile::GetInt(size_t nLine, const char * szCol, int nDefault) {
		if (nLine >= GetLineCount()) return nDefault;
		for (size_t nCol = 0; nCol < _vKeys.size(); ++nCol) {
			if (_vKeys[nCol].compare(szCol) == 0) {
				return ::atoi(_vContent[nLine][nCol].c_str());
			}
		}
		return nDefault;		
	}

	double CsvFile::GetDouble(size_t nLine, const char * szCol, double dDefault) {
		if (nLine >= GetLineCount()) return dDefault;
		for (size_t nCol = 0; nCol < _vKeys.size(); ++nCol) {
			if (_vKeys[nCol].compare(szCol) == 0) {
				return ::atof(_vContent[nLine][nCol].c_str());
			}
		}
		return dDefault;
	}

	bool CsvFile::GetBool(size_t nLine, const char * szCol, bool bDefault) {
		if (nLine >= GetLineCount()) return bDefault;
		for (size_t nCol = 0; nCol < _vKeys.size(); ++nCol) {
			if (_vKeys[nCol].compare(szCol) == 0) {
				std::string szTemp = _vContent[nLine][nCol];

				if (szTemp.compare("T") == 0 ||
					szTemp.compare("t") == 0 ||
					szTemp.compare("TRUE") == 0 ||
					szTemp.compare("true") == 0)
					return true;

				if (szTemp.compare("F") == 0 ||
					szTemp.compare("f") == 0 ||
					szTemp.compare("FALSE") == 0 ||
					szTemp.compare("false") == 0)
					return false;

				int n = ::atoi(szTemp.c_str());
				return n != 0;
			}
		}
		return bDefault;		
	}

	std::string CsvFile::GetString(size_t nLine, const char * szCol, const std::string & szDefault) {
		if (nLine >= GetLineCount()) return szDefault;
		for (size_t nCol = 0; nCol < _vKeys.size(); ++nCol) {
			if (_vKeys[nCol].compare(szCol) == 0) {
				return _vContent[nLine][nCol];
			}
		}
		return szDefault;
	}

	void CsvFile::SetInt(size_t nLine, const char * szCol, int nValue) {
		std::string szValue = String::Format("%d", nValue);
		SetString(nLine, szCol, szValue);
	}

	void CsvFile::SetDouble(size_t nLine, const char * szCol, double dValue) {
		std::string szValue = String::Format("%lf", dValue);
		SetString(nLine, szCol, szValue);
	}

	void CsvFile::SetBool(size_t nLine, const char * szCol, bool bValue) {
		std::string szValue = bValue ? "TRUE" : "FALSE";
		SetString(nLine, szCol, szValue);
	}

	void CsvFile::SetString(size_t nLine, const char * szCol, const std::string & szValue) {
		if (nLine >= GetLineCount()) return;
		for (size_t nCol = 0; nCol < _vKeys.size(); ++nCol) {
			if (_vKeys[nCol].compare(szCol) == 0) {
				_vContent[nLine][nCol] = szValue;
			}
		}
	}

	size_t CsvFile::AddLine() {
		StringVector iLine;
		for (size_t nLoop = 0; nLoop < _vKeys.size(); ++nLoop) iLine.push_back("");
		_vContent.push_back(iLine);
		return _vContent.size() - 1;
	}

}}
